#undef __KERNEL__   /* Makefile lazyness ;) */
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <stdarg.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/wait.h>           /* For waitpid */
#include <arpa/inet.h>

#include <net/if.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>

#include "libtcp_capture.h"

int tcp_capture_add(struct filter_rule_info_user *rule)
{
    int ret, sockfd;

    if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) {
        return -1;
    }
    ret = setsockopt(sockfd, IPPROTO_IP, TCP_FILTER_SO_SET_ADD, 
                     (char *)rule, sizeof(*rule));
    if (ret) {
        printf("error because: %s\n", strerror(errno));
    }

    close(sockfd);
    return ret;
}

int tcp_capture_del(struct filter_rule_info_user *rule)
{
    int ret, sockfd;

    if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) {
        return -1;
    }
    ret = setsockopt(sockfd, IPPROTO_IP, TCP_FILTER_SO_SET_DEL, 
                     (char *)rule, sizeof(*rule));

    close(sockfd);
    return ret;
}

int tcp_capture_zero(void)
{
    int ret, sockfd;

    if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) {
        return -1;
    }
    ret = setsockopt(sockfd, IPPROTO_IP, TCP_FILTER_SO_SET_ZERO, 
                     NULL, 0);
    close(sockfd);
    return ret;   
}

struct filter_get_rules *tcp_capture_get_rules(void)
{
    struct filter_get_rules *get;
    struct filter_getinfo info;
    int sockfd, ret;
    socklen_t len;

    if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) {
        return NULL;
    }
    
    len = sizeof(info);
    ret = getsockopt(sockfd, IPPROTO_IP, TCP_FILTER_SO_GET_INFO, &info, &len);
    if (ret < 0) {
        printf("TCP_FILTER_SO_GET_INFO failed\n");
        close(sockfd);
        return NULL;
    }

    len = sizeof(*get) + 
          sizeof(struct filter_rule_info_user) * info.num_rules;
    get = malloc(len);
    if (!get) {
        close(sockfd);
        return NULL;
    }
    get->num_rules = info.num_rules;
    ret = getsockopt(sockfd, IPPROTO_IP, TCP_FILTER_SO_GET_ENTRIES, get, &len);
    if (ret < 0) {
        printf("TCP_FILTER_SO_GET_ENTRIES failed\n");
        free(get);
        close(sockfd);
        return NULL;
    }

    close(sockfd);
    return get;
}

unsigned int tcp_capture_get_counter(void)
{
    struct filter_getcounter counter;
    int sockfd, ret;
    socklen_t len;

    if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) {
        return -1;
    }
    
    len = sizeof(counter);
    ret = getsockopt(sockfd, IPPROTO_IP, TCP_FILTER_SO_GET_COUNTER, &counter, &len);
    if (ret < 0) {
        close(sockfd);
        return 0;
    }
    close(sockfd);
    return counter.total_count;
}


#ifdef DEBUG

int test_add(void)
{
    struct filter_rule_info_user rule;
    int ret;

    rule.d_ip = inet_addr("192.168.10.10");
    rule.d_port = htons(80);
    return tcp_capture_add(&rule);
}
int test_get(void)
{
    struct filter_get_rules *rules;
    int ret;

    rules = tcp_capture_get_rules();
    if (!rules) {
        return -1;
    }
    if (rules->num_rules == 1) {
        if(rules->ruletable[0].d_ip == inet_addr("192.168.10.10")) {
            if (rules->ruletable[0].d_port == htons(80)) {
                free(rules);
                return 0;
            }else{
                printf("%d != %d, port didn't match.\n",
                       rules->ruletable[0].d_port, htons(80));
            }
        } else {
            printf("IP didn't match.\n");
        }
    }
    free(rules);
    return -1;
}

int test_del(void)
{
    struct filter_rule_info_user rule;
    int ret;

    rule.d_ip = inet_addr("192.168.10.10");
    rule.d_port = htons(80);
    return tcp_capture_del(&rule);
}

int test_getcounter(void)
{
    unsigned int counter;    

    counter = tcp_capture_get_counter();
    if (counter != 0) {
        return -1;
    }

    return 0;
}
    
int test_setcounter(void)
{
    return tcp_capture_zero();
}

int main(int argc, char **argv)
{
    int fail, success;

    fail = 0;
    success = 0;
    if (test_add() < 0) {
        printf("test 1 failed\n");
        fail++;
    }else{
        success++;
    }

    if (test_get() < 0) {
        printf("test 2 failed\n");
        fail++;
    }else{
        success++;
    }

    if (test_del() < 0) {
        printf("test 3 failed\n");
        fail++;
    }else{
        success++;
    }

    if (test_getcounter() < 0) {
        printf("test 4 failed\n");
        fail++;
    }else{
        success++;
    }

    if (test_setcounter() < 0) {
        printf("test 5 failed\n");
        fail++;
    }else{
        success++;
    }

    printf("total test %d success, %d failed.\n", success, fail);
    return 0;
}

#endif
